<template>
  <div :class="WIDGET_PREFIX_CLASS"
       :style="{'--gutterInterval': gutterInterval + 'px','--gutterColor': gutterColor}">
    <slot></slot>
  </div>
</template>

<script type="text/babel">
import Emitter from '../../mixins/emitter';
import Split from 'split.js'
import {
  WIDGET_PREFIX_CLASS,
  GUTTER_COLLAPSE_PREV_CLASS_NAME,
  GUTTER_COLLAPSE_NEXT_CLASS_NAME,
  ARROW,
  HIDE,
  GUTTER,
} from "../../consts/index";
import {validatenull} from "@/util/validate";

let getCollapseBtnClass = (gutterCount, className) => {
  return `${className}-${gutterCount}`;
}


let getSelector = collapseBtnClassName => {
  return "." + collapseBtnClassName + " ." + ARROW;
}

export default {
  name: 'XSplit',
  mixins: [Emitter],
  props: {
    debug: {//是否调试模式
      type: Boolean,
      default: false
    },
    storeKey: {//分隔器的key，可用来缓存位置
      type: Function,
      default: () => 'x-split_' + btoa(window.location.href)
    },
    autoSaveSize: {//是否自动保存拖动后各个分隔区域的最后的大小
      type: Boolean,
      default: true
    },
    storeSizes: {//存储拖动后各个分隔区域的最后的大小
      type: Function,
      default: (key, sizes) => {
        localStorage.setItem(key, JSON.stringify(sizes))
      },
    },
    getStoreSizes: {//获取存储拖动后各个分隔区域的最后的大小
      type: Function,
      default: key => JSON.parse(localStorage.getItem(key)),
    },
    removeStoreSizes: {//删除存储拖动后各个分隔区域的最后的大小
      type: Function,
      default: key => localStorage.removeItem(key),
    },
    direction: {//拆分方向：水平:horizontal、垂直:vertical
      type: String,
      default: 'horizontal'
    },
    cursor: {//拖动时要显示的光标。
      type: [String, Function],
      default: direction => direction === 'vertical' ? 'row-resize' : 'col-resize'
    },
    elementStyle: {//调用这个函数来设置每个被分割的区间的样式
      type: Function,
      default: undefined
    },
    gutter: {//调用这个函数来创建分隔栏
      type: Function,
      default: undefined
    },
    gutterColor: {//分隔栏颜色
      type: String,
      default: '#eee'
    },
    gutterStyle: {//调用这个函数来设置分隔栏的样式
      type: Function,
      default: undefined
    },
    gutterInterval: {//分隔栏与分隔区域之间的间隔（以像素为单位），水平分隔则是分别距离上下两侧分隔区域的间隔，垂直分隔则是分别距离左右两侧分隔区域的间隔
      type: Number,
      default: 5
    },
    gutterSize: {//分隔栏大小（以像素为单位）
      type: Number,
      default: 5
    },
    gutterAlign: {//分隔栏对齐方式
      type: String,
      default: 'center'
    },
    snapOffset: {//捕捉到以像素为单位的最小大小偏移
      type: Number,
      default: 30
    },
    dragInterval: {//每次拖动递增的像素值
      type: Number,
      default: 1
    },

    sizes: {//每个元素的初始大小（以百分比或CSS值为单位）
      type: Array,
      default: () => []
    },
    minSizes: {//每个元素的最小大小
      type: Array,
      default: () => []
    },
    maxSizes: {//每个元素的最大大小
      type: Array,
      default: () => []
    },
    expandToMin: {//将初始大小设置为`minSize`
      type: Boolean,
      default: false
    },
  },
  data() {
    return {
      WIDGET_PREFIX_CLASS,
      elements: [],
      localSizes: this.sizes,
      localMinSizes: this.minSizes,
      localMaxSizes: this.maxSizes,
      debugInfo: this.debug ? console.log : () => false,
      instance: null,
    }
  },
  computed: {
    /**
     * 分隔区域的个数
     * @returns {number}
     */
    splitAreaCount() {
      return this.localSizes.length;
    },
  },
  methods: {
    /**
     * 初始化分隔器
     * @param reset 是否是重置
     */
    init(reset = false) {
      let self = this;
      if (self.instance !== null) {
        self.instance.destroy()
      }

      let initSize = self.localSizes;
      if (!reset) {
        let cacheSizes = self.getStoreSizes(this.storeKey());
        if (cacheSizes && cacheSizes instanceof Array) {
          if (cacheSizes.length === initSize.length) {
            initSize = cacheSizes;
          }
        }
      }
      this.debugInfo('====>direction', self.direction)
      let targetCursor = self.direction === 'vertical' ? 'col-resize' : 'row-resize';
      if (self.cursor) {
        if (typeof self.cursor === 'function') {
          targetCursor = self.cursor();
        } else {
          targetCursor = self.cursor;
        }
      }

      self.instance = null;
      self.instance = Split(self.elements, {
        direction: self.direction,
        cursor: targetCursor,
        elementStyle: self.elementStyle || self.defaultElementStyle,

        gutter: self.gutter || self.defaultCreateGutter,
        gutterStyle: self.gutterStyle,
        gutterSize: self.gutterSize,
        gutterAlign: self.gutterAlign,
        snapOffset: self.snapOffset,
        dragInterval: self.dragInterval,

        sizes: initSize,
        minSize: self.localMinSizes,
        maxSize: self.localMaxSizes,

        expandToMin: self.expandToMin,

        onDrag: self.handleDrag,
        onDragStart: self.handleDragStart,
        onDragEnd: self.handleDragEnd
      });
    },
    /**
     * 默认分隔区域样式
     * @param dimension 分隔之后应该设置的css维度(水平拆分：width，垂直拆分：height)
     * @param elementSize 分隔区域的百分比大小值(不包含单位)
     * @param gutterSize 分隔栏的像素值(不包括单位)
     * @param index 分隔区域索引
     * @returns {{}}
     */
    defaultElementStyle(dimension, elementSize, gutterSize, index) {
      this.debugInfo("defaultElementStyle dimension:" + dimension + ", elementSize:" + elementSize + ", gutterSize:" + gutterSize + ", index:" + index)
      let multiple = index === 0 || index === this.splitAreaCount - 1 ? 1 : 2;
      let targetGutterSize = gutterSize + this.gutterInterval * multiple;
      this.debugInfo(`gutter ${index} split element width:`, `calc(${Math.floor(elementSize)}% - ${targetGutterSize}px)`, `gutterInterval is ${this.gutterInterval}`, `multiple is ${multiple}`)
      return {
        [dimension]: `calc(${Math.floor(elementSize)}% - ${targetGutterSize}px)`
      }
    },
    /**
     *默认分隔栏创建
     *
     * @param gutterCount 第几个分隔栏
     * @param direction 方向
     * @param pairElement  这是分隔栏后面（右边或下面）的DOM元素
     * @returns {HTMLDivElement}
     */
    defaultCreateGutter(gutterCount, direction, pairElement) {
      this.debugInfo("defaultCreateGutter====>gutterCount:", gutterCount, "direction", direction, "pairElement", pairElement)
      const gutter = document.createElement('div');

      gutter.className = `${GUTTER} ${GUTTER}-${direction}`;
      gutter.setAttribute("title", "双击重置分区大小");

      let createChildren = (parent, className, type, clickCallback) => {
        const collapseBtn = document.createElement('span');
        const arrow = document.createElement('div');

        arrow.setAttribute("title", '收起' + (direction === 'horizontal' ?
            (type === 'prev' ? '左侧' : '右侧') : (type === 'prev' ? '上侧' : '下侧')) + '区域');

        collapseBtn.className = getCollapseBtnClass(gutterCount, className);
        arrow.className = `${ARROW}`;

        //绑定事件
        collapseBtn.addEventListener('click', clickCallback);

        collapseBtn.appendChild(arrow);
        parent.appendChild(collapseBtn);
      }
      if (gutterCount === 1) {
        createChildren(gutter, GUTTER_COLLAPSE_PREV_CLASS_NAME, 'prev', e => {
          e.stopPropagation();
          e.preventDefault();
          let index = 0;
          let currentSizes = this.getSizes(true);
          this.debugInfo(`gutter:${gutterCount},target size index:${index}`, "click btn type is prev", `currentSizes:${currentSizes}`, `current sizes ${currentSizes[index]} and first split area min size is ${this.localMinSizes[index]}px`)
          if (currentSizes[index] >= this.localMinSizes[index]) {
            this.debugInfo(`gutter:${gutterCount},target size index:${index}`, "click btn type is prev", "The current first partition area has reached the minimum value")
            this.firstSplitAreaCollapse();
          }
        });
      }
      if (gutterCount === this.splitAreaCount - 1) {
        createChildren(gutter, GUTTER_COLLAPSE_NEXT_CLASS_NAME, "next", e => {
          e.stopPropagation();
          e.preventDefault();
          let index = this.splitAreaCount - 1;
          let currentSizes = this.getSizes(true);
          this.debugInfo(`gutter:${gutterCount},target size index:${index}`, "click btn type is next", `currentSizes:${currentSizes}`, `current sizes ${currentSizes[index]} and last split area min size is ${this.localMinSizes[index]}px`)
          if (currentSizes[index] >= this.localMinSizes[index]) {
            this.debugInfo(`gutter:${gutterCount},target size index:${index}`, "click btn type is next", "The current last partition area has reached the minimum value")
            this.lastSplitAreaCollapse();
          }
        })
      }

      gutter.addEventListener('dblclick', e => {
        e.stopPropagation();
        e.preventDefault();
        this.reset();
      });
      return gutter;
    },

    /**
     * 第一个分隔区域收起
     * @param doCollapse 是否执行收起操作，设置为false则只会控制按钮是否显示
     */
    firstSplitAreaCollapse(doCollapse = true) {
      let gutterCount = 1;
      let collapsePrev = document.querySelector(getSelector(getCollapseBtnClass(gutterCount, GUTTER_COLLAPSE_PREV_CLASS_NAME)));
      let collapseNext = document.querySelector(getSelector(getCollapseBtnClass(gutterCount, GUTTER_COLLAPSE_NEXT_CLASS_NAME)));

      if (collapsePrev) {
        //收起上一个区域按钮隐藏
        collapsePrev.classList.add(HIDE);
      }
      if (collapseNext) {
        //收起下一个区域按钮取消隐藏
        collapseNext.classList.remove(HIDE);
      }

      if (doCollapse) { //收起第一个分隔区域
        this.collapse(0);
      }
    },
    /**
     * 最后一个分隔区域收起
     * @param doCollapse 是否执行收起操作，设置为false则只会控制按钮是否显示
     */
    lastSplitAreaCollapse(doCollapse = true) {
      let gutterCount = this.splitAreaCount - 1;

      let collapsePrev = document.querySelector(getSelector(getCollapseBtnClass(gutterCount, GUTTER_COLLAPSE_PREV_CLASS_NAME)));
      let collapseNext = document.querySelector(getSelector(getCollapseBtnClass(gutterCount, GUTTER_COLLAPSE_NEXT_CLASS_NAME)));

      if (collapsePrev) {
        //收起上一个区域按钮取消隐藏
        collapsePrev.classList.remove(HIDE);
      }
      if (collapseNext) {
        //收起下一个区域按钮隐藏
        collapseNext.classList.add(HIDE);
      }

      if (doCollapse) { //收起最后一个分隔区域
        this.collapse(gutterCount);
      }
    },
    /**
     * 收起按钮启动隐藏
     * @param currentSizes 当前各个分隔区域的真实宽度分布数组
     */
    autoHiddenCollapseBtn(currentSizes) {
      if (currentSizes[0] - this.gutterInterval <= this.localMinSizes[0]) {
        this.firstSplitAreaCollapse(false);
      }
      if (currentSizes[this.localSizes.length - 1] - this.gutterInterval <= this.localMinSizes[this.localMinSizes.length - 1]) {
        this.lastSplitAreaCollapse(false);
      }
    },


    /**
     * 分隔栏拖动过程监听
     * @param sizes 各个分隔区域实时的size值分布
     */
    handleDrag(sizes) {
      this.$emit('onDrag', sizes);
    },
    /**
     * 分隔栏开始拖动监听
     * @param sizes 当前各个分隔区域的sizes值
     */
    handleDragStart(sizes) {
      this.$emit('onDragStart', sizes);
    },
    /**
     * 分隔栏拖动监听
     * @param sizes 当前各个分隔区域的sizes值
     */
    handleDragEnd(sizes) {
      this.storeSizes(this.storeKey(), sizes);

      this.$emit('onDragEnd', sizes);

      //显示收起按钮
      document.querySelectorAll(` .${ARROW}`).forEach(d => d.classList.remove(HIDE))

      //自动设置收起按钮是否隐藏
      this.autoHiddenCollapseBtn(this.getSizes(true));
    },

    /**
     * 重新初始化
     * @param resetElements 是否重置分隔区域
     * @private
     */
    _reInit(resetElements) {
      let self = this
      if (resetElements) {
        self.elements = [];
      }

      let index = 0;

      self.localSizes = [];
      self.localMinSizes = [];
      self.localMaxSizes = [];

      let tempSizes = [];

      self.$slots.default.forEach(vnode => {
        if (vnode.tag && vnode.tag.indexOf('SplitArea') > -1) {
          if (resetElements) {
            self.elements.push(vnode.elm);
          }
          let size = vnode.componentInstance.size;
          let minSize = vnode.componentInstance.minSize;
          let maxSize = vnode.componentInstance.maxSize;

          tempSizes.push(validatenull(size) ? (validatenull(this.sizes[index]) ? -1
              : this.sizes[index]) : size);

          self.localMinSizes.push(validatenull(minSize) ? (validatenull(this.minSizes[index]) ? 100
              : Math.max(0, this.minSizes[index] - Math.max(0, this.gutterInterval))) : minSize);

          self.localMaxSizes.push(validatenull(maxSize) ? (validatenull(this.maxSizes[index]) ? Infinity
              : this.maxSizes[index]) : maxSize);

          index++;
        }
      });
      let notSetSizeSplitAreas = tempSizes.filter(d => d === -1);
      this.debugInfo(`Number of unset size attributes is ${notSetSizeSplitAreas.length}`)
      let sum = Math.max(0, 100 - tempSizes.filter(d => d !== -1).reduce((previousValue, currentValue) => previousValue + currentValue, 0));
      this.debugInfo(`The total size attribute has been set is ${sum}`);
      for (let i = 0, len = notSetSizeSplitAreas.length, avg = Math.floor(sum / len); i < len; i++) {
        if (tempSizes[i] === -1) {
          tempSizes[i] = avg;
          this.debugInfo(`Reset the size attribute of the  ${i + 1} separating area to ${tempSizes[i]}`)
        }
      }

      self.localSizes = [...tempSizes];

      self.init();
    },


    /**
     * 当分隔区域改变是重置
     */
    changeAreaSize() {
      this._reInit(false);
    },
    /**
     * 重置
     */
    reset() {
      this.removeStoreSizes(this.storeKey());
      this.init(true)
    },
    /**
     * 设置分隔各个分割区域的大小
     * @param sizes 各分隔区域大小分布数组
     */
    setSizes(sizes) {
      this.instance.setSizes(sizes);
    },
    /**
     *获取当前各个分割区域的百分比数对应的数组
     * @param convertPercentageToSpecificValue 是否将百分比转化为具体值
     * @returns {*}
     */
    getSizes(convertPercentageToSpecificValue = false) {
      let sizes = this.instance.getSizes();
      if (!convertPercentageToSpecificValue) {
        return sizes;
      }
      //转化为具体值
      let splitContainer = this.$el;
      let bcr = splitContainer.getBoundingClientRect();
      let width = bcr.width;
      return sizes.map((d, index) => {
        let sv = d * width / 100
        if (index === sizes.length - 1) {
          //最后一个取剩余宽度，避免由于计算精度问题
          sv = width - width * (sizes.slice(0, sizes.length - 1).reduce((prevValue, currentValue) => prevValue + currentValue, 0)) / 100;
        }
        return sv;
      });


    },
    /**
     * collapse将“index”处元素的大小更改为“minSize”。除最后一个元素外，所有元素都向前（左侧或顶部）折叠。最后一个倒在后面
     * @param index
     */
    collapse(index) {
      this.instance.collapse(index);
      this.storeSizes(this.storeKey(), this.getSizes());
    },
    /**
     * 销毁实例。它删除了gutter元素和大小CSS样式Split.js集
     * 传递“preserveStyles=true”不会删除CSS样式
     * 传递“preserveGutters=true”不会删分隔栏水沟元素
     * @param preserveStyles
     * @param preserveGutters
     */
    destroy(preserveStyles = false, preserveGutters = false) {
      this.instance.destroy(preserveStyles, preserveGutters);
    },
  }
  ,
  mounted() {
    for (let i = 0, len = this.localMinSizes.length; i < len; i++) {
      this.localMinSizes[i] = Math.max(0, this.localMinSizes[i] - Math.max(0, this.gutterInterval));
    }
    this._reInit(true);

    this.$nextTick(() => {
      this.autoHiddenCollapseBtn(this.getSizes(true));
    });
  },
  watch: {
    immediate: true,
    $props: {
      deep: true,
      handler(newProps) {
        this.debugInfo("prop is change", newProps)
        this.init();
      },
    },
  }
}
</script>

<style lang="less">
@import '../../styles/index.less';
</style>

